import { EventsRes, MathTool, SceneGraph } from '@amcax/base';
import { ctx2 } from '@app-cad/module';
import {
  calculatePoint,
  distanceToLine,
  getAngle,
  getSymmetricPoint,
  RETAIN,
} from '@app-cad/common/utils';
import { wsApi } from '@app-cad/server';
import { EllipseCurve, Geometry, Vector3 } from 'three-legacy';
import {
  SKETCH_CENTEREVENTS,
  sketchEventCenter,
} from '../../../events/SketchEventCenter';
import CmdBase, { PickType } from '../../CmdBase';
import { Dimension } from '../dimension/Dimension';
import { MouseState, State } from './types';
import { AnnotationType } from '../dimension/types';

type SnapType = {
  curveId: string;
  pickType: PickType;
  point?: Vector3;
};
const dimension1 = new Dimension();
const dimension2 = new Dimension();
export default class CmdInteractor extends CmdBase {
  angle: number = 0; // 椭圆旋转角度
  center: Vector3; // 圆心
  secPoint: Vector3;
  endPoint: Vector3; // 绘制终点 ：圆上的某个点
  xRadius: number = 0; // 椭圆Xradius
  yRadius: number = 0; // 椭圆Yradius
  vecMajor: Vector3 = new Vector3(0, 1, 0); // 椭圆主轴方向
  currentStatus: MouseState = State.NONE; // 记录当前状态
  symmetricP1: Vector3 = new Vector3(); // 椭圆轴对称点
  symmetricP2: Vector3 = new Vector3(); // 椭圆轴对称点
  isExecute: boolean = false; // 执行
  private centerSnapInfo: SnapType;
  private secondSnapInfo: SnapType;

  constructor() {
    super();
    this.registerEvents();
  }

  public reset() {
    this.currentStatus = State.NONE;
    this.isExecute = false;
    this.angle = 0;
    this.center = new Vector3();
    this.endPoint = new Vector3();
    this.secPoint = new Vector3();
    this.xRadius = 0;
    this.yRadius = 0;
    this.vecMajor = new Vector3(0, 1, 0);
    this.symmetricP1 = new Vector3();
    this.symmetricP2 = new Vector3();
    this.centerSnapInfo = null;
    this.removeTempPoint();
  }

  setVerMajorAngle({ vec, angle }: { vec: Vector3; angle: number }) {
    this.vecMajor = vec;
    this.angle = angle;
    this.symmetricP1 = calculatePoint(vec, this.xRadius, this.center);
    this.symmetricP2 = calculatePoint(vec, -this.xRadius, this.center);
    this.preview();
  }

  setCenterRadius({
    centerX,
    centerY,
    radiusX,
    radiusY,
  }: {
    [key: string]: number;
  }) {
    if (
      centerX != null &&
      centerY != null &&
      radiusX != null &&
      radiusY != null
    ) {
      this.currentStatus = State.DRAWELLIPSEY;
      this.center = new Vector3(centerX, centerY, 0);
      this.xRadius = radiusX;
      this.yRadius = radiusY;
      const vec = new Vector3(this.vecMajor.y, -this.vecMajor.x, 0);
      this.endPoint = calculatePoint(vec, -radiusY, this.center);
      this.setVerMajorAngle({ vec: this.vecMajor, angle: this.angle });
      this.clearLine(['aline']);
    }
  }

  enterKeyDown = () => {
    if (this.currentStatus === State.DRAWELLIPSEX) {
      const flag = dimension1.determineDim();
      if (flag) {
        this.setLineLength();
        this.preview();
      }
    } else if (this.currentStatus === State.DRAWELLIPSEY) {
      const flag = dimension2.determineDim();
      if (flag) {
        this.setLineLength();
        this.finish();
      }
    }
  };

  setLineLength() {
    if (
      dimension1.dimType === 'create' &&
      this.currentStatus === State.DRAWELLIPSEX
    ) {
      const vec = new Vector3()
        .subVectors(this.endPoint, this.center)
        .normalize();
      this.endPoint = calculatePoint(
        vec,
        dimension1.getDimInfo().value / 2,
        this.center,
      );
      this.xRadius = this.calcRadius(
        this.center.clone(),
        this.endPoint.clone(),
      );
      this.yRadius = MathTool.fixNumber(this.xRadius * 0.45, RETAIN);
    } else if (
      dimension2.dimType === 'create' &&
      this.currentStatus === State.DRAWELLIPSEY
    ) {
      const vec = new Vector3(this.vecMajor.y, -this.vecMajor.x, 0);
      this.endPoint = calculatePoint(
        vec,
        dimension2.getDimInfo().value / 2,
        this.center,
      );
      this.yRadius = this.calcRadius(
        this.center.clone(),
        this.endPoint.clone(),
      );
    }
  }

  registerEvents = () => {
    sketchEventCenter()
      .setState('SketchEllipse')
      .on(SKETCH_CENTEREVENTS.POINTERMOVE, this.onMouseMove)
      .on(SKETCH_CENTEREVENTS.POINTERDOWN, this.onMouseDown)
      .on(SKETCH_CENTEREVENTS.KEYUP, this.onKeyUp)
      .addExit(this.destroy.bind(this));
  };
  unregisterEvents = () => {
    sketchEventCenter()
      .off(SKETCH_CENTEREVENTS.POINTERMOVE, this.onMouseMove)
      .off(SKETCH_CENTEREVENTS.POINTERDOWN, this.onMouseDown)
      .off(SKETCH_CENTEREVENTS.KEYUP, this.onKeyUp)
      .setDefault();
    dimension1?.reset();
    dimension2?.reset();
  };
  onMouseDown = (e: EventsRes) => {
    if (e.button.onLeft) {
      if (this.currentStatus === State.NONE) {
        this.center = this.mouseWV;
        if (this.pickType !== PickType.Null) {
          this.centerSnapInfo = {
            pickType: this.pickType,
            curveId: this.pickSnapInfo.id,
            point: this.mouseWV.clone(),
          };
        }
        this.createAssistPoint(this.center, null, null, true);
        this.currentStatus = State.DRAWELLIPSEX;
      } else if (this.currentStatus === State.DRAWELLIPSEX) {
        this.currentStatus = State.DRAWELLIPSEY;
        if (this.pickType !== PickType.Null) {
          this.secondSnapInfo = {
            pickType: this.pickType,
            curveId: this.pickSnapInfo.id,
            point: this.mouseWV.clone(),
          };
        }
        if (dimension1.dimType === 'preview') {
          dimension1.destroy();
        }
        this.secPoint = this.endPoint;
      } else if (this.currentStatus === State.DRAWELLIPSEY) {
        // 鼠标右键，完成
        this.finish();
      }
    } else if (e.button.onRight && !this.closeRightClick) {
      // 鼠标右键，取消绘制
      this.reset();
      this.releaseObjects();
    }
  };
  finish = async () => {
    // 鼠标右键，完成
    await this.finishDraw();
    this.reset();
    this.releaseObjects();
  };
  onMouseMove = (e: EventsRes) => {
    this.setMouseWV(e);
    if (this.currentStatus === State.DRAWELLIPSEX) {
      this.endPoint = this.mouseWV.clone();
      this.setLineLength();
      this.xRadius = this.calcRadius(
        this.center.clone(),
        this.endPoint.clone(),
      );
      this.yRadius = MathTool.fixNumber(this.xRadius * 0.45, RETAIN);
      this.preview();
    } else if (this.currentStatus === State.DRAWELLIPSEY) {
      this.endPoint = this.mouseWV.clone();
      this.setLineLength();
      this.preview();
    }
  };

  releaseObjects = () => {
    dimension1?.destroy();
    dimension2?.destroy();
    this.clearLine();
    this.assisGroup.clear();
    SceneGraph.emptyGroup(this.assisGroup);
    ctx2.viewer.requestRender();
  };

  isLoading = false;

  finishDraw = async () => {
    if (this.isLoading) {
      return;
    }

    const param = this.getParams();
    if (this.xRadius != 0) {
      try {
        this.isLoading = true;
        const res = await wsApi.createSketchEllipse(this.sketchId, param);
        await this.render(
          { added: res.added },
          param.annotations.some((a) => a != null),
        );
      } finally {
        this.isLoading = false;
      }
    }
  };

  // 添加约束
  addConstraint() {
    const constraints = [];
    if (this.centerSnapInfo?.curveId) {
      constraints.push(
        this.getConstraint(this.centerSnapInfo, this.center, false),
      );
    }
    if (this.secondSnapInfo?.curveId) {
      constraints.push(
        this.getConstraint(this.secondSnapInfo, this.secPoint, true),
      );
    }
    return constraints.filter((item) => item != null);
  }

  preview() {
    if (this.currentStatus === State.DRAWELLIPSEX) {
      this.vecMajor = new Vector3().subVectors(this.endPoint, this.center);
      this.angle = getAngle(this.endPoint, this.center); // 计算角度
      this.createUpdateLine(this.getVertices(), 'aline'); //  绘制预览线
    } else if (this.currentStatus === State.DRAWELLIPSEY) {
      if (!this.previewLines.line) {
        this.clearLine(['aline']);
        this.symmetricP1 = getSymmetricPoint(this.endPoint, this.center);
        this.symmetricP2 = this.endPoint;
      } else {
        const vec = this.symmetricP2.clone().sub(this.symmetricP1);
        this.yRadius = distanceToLine<Vector3>(this.endPoint, {
          start: this.symmetricP1,
          vec,
          normal: new Vector3(-vec.y, vec.x, 0),
        });
        const vec2 = new Vector3(vec.y, -vec.x, 0);
        this.endPoint = calculatePoint(
          this.yRadius < this.xRadius ? vec2 : vec,
          this.yRadius < this.xRadius ? this.yRadius : this.xRadius,
          this.center,
        );
      }
      this.createUpdateLine(this.getVertices());
    }
    this.drawAssist();
    ctx2.viewer.requestRender();
  }

  getVertices() {
    const ellipseCurve = new EllipseCurve(
      this.center.x,
      this.center.y, // centerX, centerY
      this.xRadius,
      this.yRadius, // xRadius, yRadius
      0,
      Math.PI * 2, // startAngle, endAngle
      false, // clockwise
      this.angle, // rotation
    );
    const geometry = new Geometry().setFromPoints(ellipseCurve.getPoints(100));
    return geometry.vertices?.length < 101 ? [] : geometry.vertices;
  }

  drawAssist() {
    const vec2 = new Vector3(this.vecMajor.y, -this.vecMajor.x, 0);
    const p1 = calculatePoint(this.vecMajor, this.xRadius, this.center);
    const p2 = calculatePoint(this.vecMajor, -this.xRadius, this.center);
    const p3 = calculatePoint(vec2, this.yRadius, this.center);
    const p4 = calculatePoint(vec2, -this.yRadius, this.center);
    this.createUpdateLine([p1, p2], 'yline', 1);
    this.createUpdateLine([p3, p4], 'xline', 1);
    if (this.currentStatus === State.DRAWELLIPSEX) {
      dimension1.dimLine([p1, p2], 1, true, 'center');
    } else if (this.currentStatus === State.DRAWELLIPSEY) {
      dimension2.dimLine([p3, p4], -1, true, 'center');
    }
  }
  getParams() {
    const maj = AnnotationType.MajorRadiusAnno;
    const min = AnnotationType.MinorRadiusAnno;
    const annotations = [
      dimension1.addDimension([], this.xRadius < this.yRadius ? min : maj, 2),
      dimension2.addDimension([], this.xRadius < this.yRadius ? maj : min, 2),
    ];
    if (this.xRadius > this.yRadius && annotations?.[1]) {
      annotations[1].points[0][0] = -annotations[1].points[0][0] || 0;
    }
    return {
      center: this.getCenter(),
      minor: Math.min(this.xRadius, this.yRadius),
      major: Math.max(this.xRadius, this.yRadius),
      majorDir:
        this.xRadius > this.yRadius
          ? [this.vecMajor.x, this.vecMajor.y]
          : [-this.vecMajor.y, this.vecMajor.x],
      curveStyle: this.lineType,
      constraints: this.addConstraint(),
      annotations: annotations,
    };
  }
  getCenter(): string | [number, number, number] {
    if (this.centerSnapInfo?.curveId) {
      const { curveId, pickType } = this.centerSnapInfo;
      if (pickType === PickType.Point) {
        return curveId;
      }
    }
    return [this.center.x, this.center.y, this.center.z];
  }
}
